(function ($) {
  var Vector = function (x, y) {
    this.x = x || 0;

    this.y = y || 0;
  };

  Vector.prototype = {
    add: function (v) {
      this.x += v.x;

      this.y += v.y;

      return this;
    },

    length: function () {
      return Math.sqrt(this.x * this.x + this.y * this.y);
    },

    rotate: function (theta) {
      var x = this.x;

      var y = this.y;

      this.x = Math.cos(theta) * this.x - Math.sin(theta) * this.y;

      this.y = Math.sin(theta) * this.x + Math.cos(theta) * this.y;

      //this.x = Math.cos(theta) * x - Math.sin(theta) * y;

      //this.y = Math.sin(theta) * x + Math.cos(theta) * y;

      return this;
    },

    mult: function (f) {
      this.x *= f;

      this.y *= f;

      return this;
    },
  };

  var Leaf = function (p, r, c, ctx) {
    this.p = p || null;

    this.r = r || 0;

    this.c = c || "rgba(255,255,255,1.0)";

    this.ctx = ctx;
  };

  Leaf.prototype = {
    render: function () {
      var that = this;

      var ctx = this.ctx;

      var f = Branch.random(1, 2);

      for (var i = 0; i < 5; i++) {
        (function (r) {
          setTimeout(function () {
            ctx.beginPath();

            ctx.fillStyle = "red";

            ctx.moveTo(that.p.x, that.p.y);

            ctx.arc(that.p.x, that.p.y, r, 0, Branch.circle, true);

            ctx.fill();
          }, r * 60);
        })(i);
      }
    },
  };

  var Branch = function (p, v, r, c, t) {
    this.p = p || null;

    this.v = v || null;

    this.r = r || 0;

    this.length = 0;

    this.generation = 1;

    this.tree = t || null;

    this.color = c || "rgba(255,255,255,1.0)";

    this.register();
  };

  Branch.prototype = {
    register: function () {
      this.tree.addBranch(this);
    },

    draw: function () {
      var ctx = this.tree.ctx;

      ctx.beginPath();

      ctx.fillStyle = 'green';

      ctx.moveTo(this.p.x, this.p.y);

      ctx.arc(this.p.x, this.p.y, this.r, 0, Branch.circle, true);

      ctx.fill();
    },

    modify: function () {
      var angle = 0.14 - 0.1 / this.generation;

      this.p.add(this.v);

      this.length += this.v.length();

      this.r *= 0.99;

      this.v.rotate(Branch.random(-angle, angle));

      if (this.r < 0.8 || this.generation > 10) {
        this.tree.removeBranch(this);

        var l = new Leaf(this.p, 10, this.color, this.tree.ctx);

        l.render();
      }
    },

    grow: function () {
      this.draw();

      this.modify();

      this.fork();
    },

    fork: function () {
      var p = this.length - Branch.random(100, 200); // + (this.generation * 10);

      if (p > 0) {
        var n = Math.round(Branch.random(1, 3));

        this.tree.stat.fork += n - 1;

        for (var i = 0; i < n; i++) {
          Branch.clone(this);
        }

        this.tree.removeBranch(this);
      }
    },
  };

  Branch.circle = 2 * Math.PI;

  Branch.random = function (min, max) {
    return Math.random() * (max - min) + min;
  };

  Branch.clone = function (b) {
    var r = new Branch(
      new Vector(b.p.x, b.p.y),
      new Vector(b.v.x, b.v.y),
      b.r,
      b.color,
      b.tree
    );

    r.generation = b.generation + 1;

    return r;
  };

  Branch.rgba = function (r, g, b, a) {
    return "rgba(" + r + "," + g + "," + b + "," + a + ")";
  };

  Branch.randomrgba = function (min, max, a) {
    return Branch.rgba(
      Math.round(Branch.random(0, 125)),
      Math.round(Branch.random(255, 255)),
      Math.round(Branch.random(0, 0)),
      a
    );
  };

  var Tree = function () {
    var branches = [];

    var timer;

    this.stat = {
      fork: 0,

      length: 0,
    };

    this.addBranch = function (b) {
      branches.push(b);
    };

    this.removeBranch = function (b) {
      for (var i = 0; i < branches.length; i++) {
        if (branches[i] === b) {
          branches.splice(i, 1);

          return;
        }
      }
    };

    this.render = function (fn) {
      var that = this;

      timer = setInterval(function () {
        fn.apply(that, arguments);

        if (branches.length > 0) {
          for (var i = 0; i < branches.length; i++) {
            branches[i].grow();
          }
        } else {
          //clearInterval(timer);
        }
      }, 1000 / 30);
    };

    this.init = function (ctx) {
      this.ctx = ctx;
    };

    this.abort = function () {
      branches = [];

      this.stat = {
        fork: 0,

        length: 0,
      };
    };
  };

  function init() {
    // init

    var $window = $(window);

    var $body = $("body");

    var canvas_width = $window.width();

    var canvas_height = $window.height();

    var center_x = canvas_width;

    var stretch_factor = 600 / canvas_height;

    var y_speed = 3 / stretch_factor;

    // tx

    var canvas = $("#banner-canvas")[0];

    canvas.width = canvas_width * 2;

    canvas.height = canvas_height;

    var ctx = canvas.getContext("2d");

    ctx.globalCompositeOperation = "lighter";

    // tree

    var t = new Tree();

    t.init(ctx);

    for (var i = 0; i < 14; i++) {
      new Branch(
        new Vector(center_x, canvas_height),
        new Vector(Math.random(-1, 1), -y_speed),
        14 / stretch_factor,
        Branch.randomrgba(0, 255, 1),
        t
      );
    }

    t.render(function () {});
  }

  $(function () {
    init();
  });
})(window.jQuery);
